/*
* ============================================================================
*  Name        : sea_object.h
*  Part of     : Finite state machine utilities
*  Description : object definition
*  Version     : %version: %
*
*
* ============================================================================
*/
#if !defined( __sea_object_h__ )
#define __sea_object_h__

#include "sea_config.h"
#include "sea_types.h"
#include "sea_atomic_op.h"
#include "sea_association.h"

/**
 * @file sea_object.h
 * @brief
 * @ref sea::Object
 *
 * @ingroup AssociationUtilities
 */

/**
 * @namespace sea
 */
namespace sea
{
    /**
     * Forward definitions
     */
    class Link;
    class OsMutex;
    class Object;

    /**
     * @class ref
     * @brief sea reference object. Object container.
     * Eliminates the need for explicit reference counter tracking.
     *
     * @ingroup AssociationUtilities
     */
    template< typename T = Object >
    class ref
    {
    public:
        inline ref( T* obj = 0, bool add_ref = true );
        inline ref( const ref< T >& obj );
        inline ~ref( );

        template< typename TYPE > 
        TYPE* as( ) { return dynamic_cast< TYPE* > ( m_obj ); }

        T* operator ->( ) { return reinterpret_cast< T* >( m_obj ); }
        T* operator *( ) { return reinterpret_cast< T* >( m_obj ); }
        T* get( ) { return reinterpret_cast< T* >( m_obj ); }

        bool operator == ( T* obj ) { return m_obj == obj; }
        bool operator == ( ref< T >& ptr ) { return m_obj == ptr.get( ); }
        bool operator != ( T* obj ) { return m_obj != obj; }
        bool operator != ( ref< T >& ptr ) { return m_obj != ptr.get( ); }
        bool operator !( ) { return m_obj == NULL; }

        inline void adopt( const ref< T >& obj );
        inline void adopt( T* obj );
        void operator << ( const ref< T >& obj ) { adopt( obj ); }
        void operator << ( T* obj ) { adopt( obj ); }

        void reset( ) { adopt( NULL ); }

        inline ref< T >& operator = ( const ref< T >& right );
        inline ref< T >& operator = ( Object* obj );

        inline bool via( Association id, ref< T >& the_ref );
        inline bool operator += ( ref< T >& the_ref );
        inline bool operator -= ( ref< T >& the_ref );
        inline bool operator( )( ref< T >& obj, Association id = Attribute );

        inline bool has( ref< T >& obj );
        bool linked( ref< T >& obj ){ return has( obj ); }
        bool is( ref< T >& obj ){ return has( obj ); }
        bool does( ref< T >& obj ){ return has( obj ); }
        bool can( ref< T >& obj ){ return has( obj ); }

        inline bool has_no( ref< T >& obj );
        bool does_not( ref< T >& obj ) { return has_no( obj ); }
        bool is_not( ref< T >& obj ) { return has_no( obj ); }
        bool can_not( ref< T >& obj ){ return has_no( obj ); }

    private:
        Object* m_obj;
    };

    /**
     * @class aref
     * @brief sea container object.
     * Eliminates the need of explicit reference counter tracking.
     *
     * @ingroup AssociationUtilities
     */
    class aref : public ref< >
    {
    public:
        aref( const aref& obj ) : ref< >( const_cast< aref& > ( obj ).as< Object >( ) ) { }
        aref( const ref< >& obj ) : ref< >( const_cast< ref< >& > ( obj ).as< Object >( ) ) { }
        aref( Object* obj = 0, bool add_ref = true ) : ref< >( obj, add_ref ){ }
        aref( char* name ) { aref::operator << ( name ); }
        aref( const char* name ) { aref::operator << ( name ); }
        aref( i::uint32 number ) { aref::operator << ( number ); }

        void operator << ( char* name );
        void operator << ( const char* name );
        void operator << ( i::uint32 number );
        void operator << ( const ref< >& obj ) { ref< >::operator << ( obj ); }
        void operator << ( const aref& obj ) { ref< >::adopt( const_cast< aref& >( obj ).get( ) ); }
        void operator << ( Object* obj ) { ref< >::adopt( obj ); }

        aref& operator = ( const ref< >& right ) { ref< >::operator = ( right ); return *this; }
        aref& operator = ( Object* obj ) { ref< >::operator = ( obj ); return *this; }
    };

    /**
     * @class Object
     * @brief Base class.
     *      Alows multiple associations between derivatives of @ref sea::Object
     *      \par Example:
     *          @li \c Apple is associated with qualities: Fruit, RoundShaped, Edable.
     *          @li \c Apple is aware of the associations it is a part of. See example_101.cpp
     *
     *      Object keeps track of memory allocations <br>
     *      Object can have "right" and "left" associations. It is up to application to interpret the meaning <br>
     *      Example: Parent -> child, Before -> After, Hierarchy, etc.
     *
     * @ingroup AssociationUtilities
     */
    class Object
    {
        friend class Association;

    public:
        Object( ):
            m_link( 0 ),
            m_ref_cnt( 1 )
            {
                m_u.m_data = 0;
            }

        Object( i::uint32 data ) :
            m_link( 0 ),
            m_ref_cnt( 1 )
            {
                m_u.m_word = data;
            }

        Object( void* data ) :
            m_link( 0 ),
            m_ref_cnt( 1 )
            {
                m_u.m_data = data;
            }

        Object( char* data ) :
            m_link( 0 ),
            m_ref_cnt( 1 )
            {
                m_u.m_data = reinterpret_cast< void* >( data );
            }

        Object( const char* data ) :
            m_link( 0 ),
            m_ref_cnt( 1 )
            {
                m_u.m_cdata = reinterpret_cast< const void* >( data );
            }

        Object( Object* data ) :
            m_link( 0 ),
            m_ref_cnt( 1 )
            {
                m_u.m_obj = data;
            }

        virtual ~Object( ){ }

        /**
         * Overloaded new
         */
        #if defined( DEBUG ) && defined( MEM_TRACKING )
            void* operator new( unsigned int size, int line = __LINE__, const char* file = __FILE__ ) NOTHROW;
        #else
            void* operator new( unsigned int size ) NOTHROW;
        #endif

        /**
         * Overloaded dummy delete. New is NOTHROW...eh?
         */
        #if defined( DEBUG ) && defined( MEM_TRACKING )
            void operator delete( void* ptr, int line, const char* file ) NOTHROW
            {
                SEA_UNUSED( ptr );
                SEA_UNUSED( line );
                SEA_UNUSED( file );
            }
        #endif

        /**
         * Overloaded new[]
         */
        #if defined( DEBUG ) && defined( MEM_TRACKING )
            void* operator new[ ]( unsigned int size, int line = __LINE__, const char* file = __FILE__ ) NOTHROW;
        #else
            void* operator new[ ]( unsigned int size ) NOTHROW;
        #endif

        /**
         * Overloaded delete
         */
        void operator delete( void* ptr ) NOTHROW;

        /**
         * Overloaded delete[ ]
         */
        void operator delete[ ]( void* ptr ) NOTHROW;

        /**
         * Get pointer to an object. Increments refrence counter.
         *
         * @return Object pointer
         */
        Object* get_ref( )
        {
            m_ref_cnt.inc( );
            return this;
        }

        /**
         * Release pointer to an object. Decrements reference counter.
         *
         * @return Reference counter
         */
        int release_ref( )
        {
            if ( m_ref_cnt.dec( ) == 0 )
            {
                delete this;
                return 0;
            }
            return m_ref_cnt.get( );
        }

        /**
         * Get current reference counter
         *
         * @return Reference counter value
         */
        int refs( ) { return m_ref_cnt.get( ); }

        /**
         * Add object to linked list after obj. Supports poligamy.
         *
         * @param obj Pointer to an object
         * @param id Association id
         * @param unique "I know it is unique". If true, skips the search step for performance boost
         *
         * @return @li true     if object linked successfully
         * @return @li false    otherwise.
         */
        virtual bool attach_right_of( Object* obj, Association* id, bool unique = false );
        virtual bool attach_right_of( Object* obj, Association id, bool unique = false ) { return attach_right_of( obj, &id, unique ); }
        virtual bool attach_right_of( sea::ref< >& obj, Association id, bool unique = false ) { return attach_right_of( obj.get( ), &id, unique ); }
        virtual bool attach_right_of( sea::ref< >& obj, sea::ref< >& association, bool unique = false ) { return attach_right_of( obj.get( ), &Association( association.get( ) ), unique ); }

        /**
         * Add object to linked list before obj. Supports poligamy.
         *
         * @param obj Pointer to an object or reference object
         * @param id Association id
         * @param unique "I know it is unique". If true, skips the search step for performance boost
         *
         * @return @li true     if object linked successfully
         * @return @li false    otherwise.
         */
        virtual bool attach_left_of( Object* obj, Association* id, bool unique = false );
        virtual bool attach_left_of( Object* obj, Association id = ( int )Attribute, bool unique = false ) { return attach_left_of( obj, &id, unique ); }
        virtual bool attach_left_of( ref< >& obj, Association id = ( int )Attribute, bool unique = false ) { return attach_left_of( obj.get( ), &id, unique ); }
        virtual bool attach_left_of( ref< >& obj, ref< >& association, bool unique = false ) { return attach_left_of( obj.get( ), &Association( association.get( ) ), unique ); }

        /**
         * Insert an object on the right of obj. Does not support polygamy.
         *
         * @param obj Pointer to an object or reference object
         * @param id Association id
         *
         * @return @li true     if object linked successfully
         * @return @li false    otherwise.
         */
        virtual bool insert_right_of( Object* obj, Association* id );
        virtual bool insert_right_of( Object* obj, Association id = ( int )Attribute ) { return insert_right_of( obj, &id ); }
        virtual bool insert_right_of( ref< >& obj, Association id = ( int )Attribute ) { return insert_right_of( obj.get( ), &id ); }

        /**
         * Insert an object on the left of obj. Does not support polygamy.
         *
         * @param obj Pointer to an object or reference object
         * @param id Association id
         *
         * @return @li true     if object linked successfully
         * @return @li false    otherwise
         */
        virtual bool insert_left_of( Object* obj, Association* id );
        virtual bool insert_left_of( Object* obj, Association id = ( int )Attribute ) { return insert_left_of( obj, &id ); }
        virtual bool insert_left_of( ref< >& obj, Association id = ( int )Attribute ) { return insert_left_of( obj.get( ), &id ); }

        /**
         * Add quality/attribute to the object
         *
         * @param obj Quality/attribute object
         *
         * @return @li true     if object associated successfully
         * @return @li false    otherwise
         */
        bool has( Object* obj )
        {
            // Prevent duplicated linkage
            if( is_linked( obj ) )
            {
                return true;
            }
            return attach_right_of( obj, Association( ( int )Attribute ) );
        }

        /**
         * Synonym of @ref has
         */
        bool linked( Object& obj ){ return has( &obj ); }

        /**
         * Synonym of @ref has
         */
        bool has( Object& obj ){ return has( &obj ); }

        /**
         * Synonym of @ref has
         */
        bool is( Object* obj ){ return has( obj ); }

        /**
         * Synonym of @ref has
         */
        bool is( Object& obj ){ return has( &obj ); }

        /**
         * Synonym of @ref has
         */
        bool operator += ( Object* obj ){ return has( obj ); }

        /**
         * Synonym of @ref has
         */
        bool operator += ( Object& obj ){ return has( &obj ); }

        /**
         * Remove quality/attribute from the object
         *
         * @param obj Quality/attribute object
         *
         * @return @li true     if object detached successfully
         * @return @li false    otherwise
         */
        bool has_no( Object* obj ) { return detach( obj, Association( ( int )Attribute ) ); }

        /**
         * Remove quality/attribute from the object
         */
        bool has_no( Object& obj ){ return has_no( &obj ); }

        /**
         * Synonym of @ref has_no
         */
        bool does_not( Object* obj ){ return has_no( obj ); }

        /**
         * Synonym of @ref has_no
         */
        bool does_not( Object& obj ){ return has_no( &obj ); }

        /**
         * Synonym of @ref has_no
         */
        bool is_not( Object* obj ){ return has_no( obj ); }

        /**
         * Synonym of @ref has_no
         */
        bool is_not( Object& obj ){ return has_no( &obj ); }

        /**
         * Synonym of @ref has_no
         */
        bool operator -= ( Object* obj ){ return has_no( obj ); }

        /**
         * Synonym of @ref has_no
         */
        bool operator -= ( Object& obj ){ return has_no( &obj ); }

        /**
         * Check if object has a link with another object by the association
         *
         * @param obj Object pointer
         * @param id Association id
         *
         * @return @li true
         *         @li false
         */
        virtual bool is_linked( Object* obj, Association id = Attribute );

        /**
         * Check if object has a link with another object by the association
         *
         * @param obj Object reference
         * @param id Association id
         *
         * @return @li true
         *         @li false
         */
        virtual bool is_linked( Object& obj, Association id = Attribute ) { return is_linked( &obj, id ); }

        /**
         * Check if object has an attribute/quality
         *
         * @param obj Object pointer
         * @param id Association id
         *
         * @return @li true
         *         @li false
         */
        bool operator( )( Object* obj, Association id = Attribute ){ return is_linked( obj, id ); }

        /**
         * Check if object has an attribute/quality
         *
         * @param obj Object pointer
         * @param id Association id
         *
         * @return @li true
         *         @li false
         */
        bool operator( )( Object& obj, Association id = Attribute ){ return is_linked( &obj, id ); }

        /**
         * Check if object has an attribute/quality
         *
         * @param obj Object pointer
         * @param id Association id
         *
         * @return @li true
         *         @li false
         */
        bool of( Object* obj, Association id = Attribute ){ return is_linked( obj, id ); }

        /**
         * Check if object has an attribute/quality
         *
         * @param obj Object pointer
         * @param id Association id
         *
         * @return @li true
         *         @li false
         */
        bool of( Object& obj, Association id = Attribute ){ return is_linked( &obj, id ); }

        /**
         * Detach an object from all associations specified by the id
         *
         * @param id Association id
         *
         * @return @li true     if object detached successfully
         * @return @li false    not linked
         */
        virtual bool detach( Association id );

        /**
         * Remove an object from another object by the association
         *
         * @param obj Object to detach from
         * @param id Association id
         *
         * @return @li true     if object detached successfully
         * @return @li false    not linked
         */
        virtual bool detach( Object* obj, Association id );

        /**
         * Remove an object from all associations
         *
         * @return @li true     on success
         */
        virtual bool detach_all( ){ return detach( Object::m_any_id ); }

        /**
         * Get Right neighbour
         *
         * @param id Association id
         *
         * @return Right neighbour object pointer
         */
        virtual sea::ref< > get_right_obj( Association id ) ;

        /**
         * Get Left neighbour
         *
         * @param id Association id
         *
         * @return Left neighbour object pointer
         */
        virtual sea::ref< > get_left_obj( Association id );

        /**
         * Set data
         *
         * @param data uint32
         */
        void set_data_int( i::uint32 data ) {  m_u.m_word = data; }

        /**
         * Set data
         *
         * @param data pointer to data object
         */
        template < typename T > void set_data( T* data ) {  m_u.m_data = reinterpret_cast< void* >( data ); }

        /**
         * Set data
         *
         * @param data pointer to data object
         */
        template < typename T > void set_const_data( const T* data ) {  m_u.m_cdata = reinterpret_cast< const void* >( data ); }

        /**
         * Get any data
         *
         * @return Retrive data as integer
         */
        i::uint32 as_int( ){ return m_u.m_word; }

        /**
         * Get any data
         *
         * @return Retrive data object
         */
        template < typename T > T* as( ){ return reinterpret_cast< T* >( m_u.m_data ); }

        /**
         * Get link pointer by association id. If id == -1 first link will be returned.
         *
         * @param id Association id
         *
         * @return Link object pointer
         */
        virtual sea::ref< Link > get_link( Association id = -1 );

        /**
         * Get first link
         *
         * @return First/root link/association
         */
        virtual sea::ref< Link > get_first_link( ) { return m_link; }

        /**
         * Set new first link. Needed to sort object associations
         *
         * @param link Set new first link/association
         *
         * @return First/root link/association
         */
        virtual void set_first_link( Link* link ) { m_link = link; }

        /**
         * Virtual method for thread safe derivative of the Object class
         *
         * @return NULL Lock is not defined at this level
         * virtual OsMutex* lock( ) { return NULL; }
         */

    private:

        /**
         * Create new link
         *
         * @param id Association id
         *
         * @return Link object pointer
         */
        sea::ref< Link > create_new_link( Association id );

        /**
         * Remove link from object's associations
         *
         * @param link Existing link in the list of object's associations
         */
        void remove_link( Link* link );

    private:
        sea::ref< Link > m_link;            ///< Object link. See Link class
        OsAtomic m_ref_cnt;                 ///< Reference counter
        union
        {
            i::uint32 m_word;
            void* m_data;
            const void* m_cdata;
            Object* m_obj;
        } m_u;                              ///< Payload

    protected:
        static Association m_any_id;        ///< Association id ANY ( performance )
    };


    /**
     * ref< T > class inline functions implementation
     */
    template< typename T >
    ref< T >::ref( T* obj, bool add_ref ):
        m_obj( reinterpret_cast< Object* >( obj ) )
    {
        if ( add_ref )
        {
            if ( m_obj )
            {
                m_obj->get_ref( );
            }
        }
    }

    template< typename T >
    ref< T >::ref( const ref< T >& obj ):
        m_obj( obj.m_obj )
    {
        if ( m_obj )
        {
            m_obj->get_ref( );
        }
    }

    template< typename T >
    ref< T >::~ref( )
    {
        if ( m_obj )
        {
            m_obj->release_ref( );
        }
    }

    template< typename T >
    void ref< T >::adopt( const ref< T >& obj )
    {
        if ( m_obj != obj.m_obj )
        {
            if ( m_obj )
            {
                m_obj->release_ref( );
            }
            m_obj = obj.m_obj;
        }
    }

    template< typename T >
    void ref< T >::adopt( T* obj )
    {
        if ( m_obj != reinterpret_cast< Object* >( obj ) )
        {
            if ( m_obj )
            {
                m_obj->release_ref( );
            }
            m_obj = reinterpret_cast< Object* >( obj );
        }
    }

    template< typename T >
    ref< T >& ref< T >::operator = ( const ref< T >& right )
    {
        if ( m_obj != right.m_obj )
        {
            if ( m_obj )
            {
                m_obj->release_ref( );
            }

            m_obj = right.m_obj;

            if ( m_obj )
            {
                m_obj->get_ref( );
            }
        }

        return *this;
    }

    template< typename T >
    ref< T >& ref< T >::operator = ( Object* obj )
    {
        if ( m_obj != obj )
        {
            if ( m_obj )
            {
                m_obj->release_ref( );
            }

            m_obj = obj;

            if ( m_obj )
            {
                m_obj->get_ref( );
            }
        }
        return *this;
    }

    template< typename T >
    bool ref< T >::operator += ( ref< T >& the_ref )
    {
        return m_obj ? m_obj->has( the_ref.get( ) ) : false;
    }

    template< typename T >
    bool ref< T >::via( Association id, ref< T >& the_ref )
    {
        return m_obj ? m_obj->attach_right_of( the_ref.get( ), id ) : false;
    }

    template< typename T >
    bool ref< T >::operator -= ( ref< T >& the_ref )
    {
        return m_obj ? m_obj->has_no( the_ref.get( ) ) : false ;
    }

    template< typename T >
    bool ref< T >::operator( )( ref< T >& obj, Association id )
    {
        return m_obj ? m_obj->is_linked( obj.get( ), id ) : false;
    }

    template< typename T >
    bool ref< T >::has( ref< T >& obj )
    {
        return m_obj ? m_obj->has( obj.get( ) ) : false;
    }

    template< typename T >
    bool ref< T >::has_no( ref< T >& obj )
    {
        return m_obj->detach( obj.get( ), Association( ( int )Attribute ) );
    }
}

#endif
